///--------------------------------------------------
/**
 *
 *      Hand Gesture Recognition System
 *
 */
//--------------------------------------------------
/**
 * //DESCRIPTORPRO CLASS//
 * Procesa los puntos descriptores de un Frame.
 *
 * @author  Edwin Cobos & Cesar Murcia & Wilson Sarmiento
 * @version HGR_v1.5
 *
 * Revisado: 22/02/12
 */
//--------------------------------------------------
package HGR;
//IMPORT//
import SIFT.SIFT_ExtractPointRoi;
import SIFT.util.Feature;
import SIFT.util.FloatArray2D;
import hmm.Model;
import java.util.ArrayList;
import java.util.List;
//--------------------------------------------------
/**
 *Procesa los puntos descriptores de un Frame, Heredada de la clase Thread de Java
 */
public class DescriptorPro extends Thread
{
    //--------------------------------------------------
    private Frame ActualFrame;
    private boolean Alive;
    private FloatArray2D Desc;
    private SIFT_ExtractPointRoi sift;
    private List<Feature> ipts;
    private Descriptor Descriptors;
    private int Angle;
    private float ThresholdDesc = 0.9f;
    
    //--------------------------------------------------
    /**
     * Constructor Parametrico
     * @param img Imagen a procesar
     */
    public DescriptorPro(Frame img)
    {
        ActualFrame = new Frame(img.getBuffer(), img.getIndice(), img.getWidth(), img.getHeight());
        Angle = 15;
        Alive = true;
    }    
    //--------------------------------------------------
    /**
     * Ejecuta el proceso
     */
    @Override
    public void run()
    {        
        Desc = new FloatArray2D(ActualFrame.getBuffer(), ActualFrame.getWidth(), ActualFrame.getHeight());
        sift = new SIFT_ExtractPointRoi();
        sift.run(Desc);
        
        ipts = sift.getLastResult();

        System.out.println(ipts.size()+ " PUNTOS " + ActualFrame.getIndice());

        //if(ipts.size() > 15)
        //{
            //System.out.println(ActualFrame.getIndice() +" " + HGR_gui.GetGestureActive());

            if((ActualFrame.getIndice()-HGR_gui.GetGestureActive())<10 && ipts.size() > 10)
            {
               // System.out.println(HGR_gui.GetHistogramsLenght() + " --------- " + HGR_gui.GetHistogramsSpeed() + " --------- " +ipts.size());
                Descriptors = new Descriptor(ipts, ActualFrame.getIndice());
                if( HGR_gui.GetHistogramsLenght() < HGR_gui.GetHistogramsSpeed())
                {
                    HGR_gui.SetDescriptor(Descriptors, HGR_gui.GetHistogramsLenght());
                    HGR_gui.SetHistogramsLenght( HGR_gui.GetHistogramsLenght() + 1);
                }
                else
                {
                    if( HGR_gui.GetHistogramsLenght() == HGR_gui.GetHistogramsSpeed())
                    {
                        HistogramsCalc();
                        HGR_gui.clearDescriptor();
                        HGR_gui.SetHistogramsLenght(0);
                    }
                }
                HGR_gui.out_state.setText("Capturando...");
            }
            else
            {
                //HMM run secuence
                if(HGR_gui.GetObservations().size() > 0)
                {
                    //int pmax = Probabilities();
                    HGR_gui.out_state.setText("Calculando...");
                    
                    /* aum  0
                     * dism 1
                     * esc  2
                     * rot  3
                     * tra  4
                     * x    5
                     * y    6
                     * z    7
                     */
                            
                            
                    //caso 0
                    if(HGR_gui.Com_tra.isSelected())               
                    {
                        Model[] m0 ={HGR_gui.getModels(2),HGR_gui.getModels(3),HGR_gui.getModels(4)};
                        int pmax = Probabilities(m0);
                        HGR_gui.Gesture(pmax+2);                        
                    }
                    //caso 1
                    if(HGR_gui.Com_eje.isSelected())               
                    {
                        Model[] m1 ={HGR_gui.getModels(5),HGR_gui.getModels(6),HGR_gui.getModels(7)};
                        int pmax = Probabilities(m1);
                        HGR_gui.Gesture(pmax+5);                        
                    }
                    //caso 2
                    if(HGR_gui.Com_aum.isSelected())               
                    {
                        Model[] m2 ={HGR_gui.getModels(0),HGR_gui.getModels(1)};
                        int pmax = Probabilities(m2);
                        HGR_gui.Gesture(pmax);                        
                    }                    
                    //accion(0-7)
                    //HGR_gui.Gesture(null);
                    
                    
                }
                HGR_gui.ClearObservations();
                HGR_gui.clearDescriptor();
                HGR_gui.SetHistogramsLenght(0);

            }
            
            HGR_gui.SetGestureActive(ActualFrame.getIndice());
        /*}
        else
        {
            HGR_gui.SetHistogramsLenght(0);
            HGR_gui.out_state.setText("En Espera...");
        }*/
        Alive = false;
        HGR_gui.count -= 1;
    }
    //--------------------------------------------------
    public void HistogramsCalc()
    {
        float Histo[] = new float[360/Angle];
        int anglemax;

        for(int i = 1; i< HGR_gui.GetHistogramsSpeed() ; i++)
        {
            Histogram(Histo, HGR_gui.GetDescriptor(i-1).GetDescriptor(), HGR_gui.GetDescriptor(i).GetDescriptor(), Angle);
        }
        System.out.println();
        //Guardar Angulo
        anglemax = hmm.Hmm.PMax(Histo);
        HGR_gui.SetObservations(anglemax);
        System.out.println(anglemax*Angle + "angulo");

    }
    //--------------------------------------------------
    /**
     * Evalúa los descriptores de los puntos comparándolos y estableciendo su relación de
     * acuerdo a su similaridad, determinando el movimiento de los mismos a través del tiempo
     * y elabora un histograma que muestra las transiciones de acuerdo a los ángulos entre los
     * puntos similares.
     * @param Histograms Arreglo donde se guardan los histogramas de acuerdo a los diferentes ángulos establecidos
     * @param Puntos Almacena los puntos de los frames en el tiempo para la comparación
     * @param FPoints Almacena los puntos del frame actual para la comparación     
     * @param DivAngles Número de divisiones de los ángulos que determina el número de muestras en el histograma
     * @param h Número de ángulo actual
     */
    public void Histogram(float[] Histograms, ArrayList<Feature> Puntos, ArrayList<Feature> FPoints, int DivAngles)
    {

        float D = 0.0f;
        float Best_D = Float.MAX_VALUE;
        float Best_D2 = Float.MAX_VALUE;
        int Best_ind = -1;
        int i;

        float Angulo = 0.0f;
        float difY = 0.0f;
        float difX = 0.0f;

        int LimUp = 0;
        int LimDown = 0;
        int a;
        //Para todos los puntos
        int p;
        if(Puntos.size() > 0)
        {
            for(p=0; p<FPoints.size(); p++)
            {
                D = 0.0f;
                Best_D = Float.MAX_VALUE;
                Best_D2 = Float.MAX_VALUE;
                Best_ind = -1;

                for(i=0; i<Puntos.size(); i++)
                {
                    //Calcular la distancia entre descriptores
                    D = kNN(FPoints.get(p).descriptor, Puntos.get(i).descriptor);
                    //Determinar la similaridad y asegurar que no hallan falsos positivos a traves de una segunda muestra
                    if(D < Best_D)
                    {
                        Best_D2 = Best_D;
                        Best_D = D;
                        Best_ind = i;
                    }else if(D < Best_D2)
                        Best_D2 = D;
                }

                //Si hay match entre dos puntos calcula el angulo y lo guarda en el histograma correspondiente
                if(Best_ind >= 0 && Best_D2 < Float.MAX_VALUE && Best_D/Best_D2 < ThresholdDesc)
                {
                    Angulo = 0.0f;
                    difY = 0.0f;
                    difX = 0.0f;


                    difX = FPoints.get(p).location[0] - Puntos.get(Best_ind).location[0];

                    difY = FPoints.get(p).location[1] - Puntos.get(Best_ind).location[1];

                    //Delta del descriptor 2% del frame
                    int Th_distX = 13;
                    int Th_distY = 10;
                    double dst = Math.sqrt(Math.pow(Th_distX,2) + Math.pow(Th_distY,2));
                    double dst1 = Math.sqrt(Math.pow(difX,2) + Math.pow(difY,2));
                    if(dst>dst1)
                    {
                        break;
                    }

                    Angulo = (float)Math.toDegrees(Math.atan2((difY),(difX)));
                    if(Angulo < 0.0f){
                        Angulo = 360 + Angulo;
                    }

                    LimUp = 0;
                    LimDown = 0;
                    for(a=0; a<(360/DivAngles); a++)
                    {
                        if(DivAngles%2 != 0)
                        {
                            LimUp = (int)Math.floor(((DivAngles*(a+1))+DivAngles/2))+1;
                            LimDown = (int)Math.floor((DivAngles/2)+DivAngles*a);
                        }
                        else
                        {
                            LimUp = (DivAngles*(a+1))+DivAngles/2;
                            LimDown = (DivAngles/2)+DivAngles*a;
                        }

                        if((Angulo > LimDown) && Angulo <= LimUp)
                        {
                            Histograms[((DivAngles*(a+1))/DivAngles)]++;
                            break;
                        }

                        if(DivAngles%2 != 0)
                        {
                            LimUp = (int)Math.floor((DivAngles/2.0))+1;
                            LimDown = (int)Math.floor(360-(DivAngles/2));
                        }
                        else
                        {
                            LimUp = DivAngles/2;
                            LimDown = 360-(DivAngles/2);
                        }

                        if((Angulo > LimDown) || Angulo <= LimUp )
                        {
                            Histograms[0]++;
                            break;
                        }
                    }
                }// FIN Si hay match
            }// FIN para puntos nuevos
        }// FIN si no hay puntos
    }
    //--------------------------------------------------
    /**
     * Obtiene la distancia entre dos descriptores utilizando el NEAREST NEIGHBOR METHOD kNN
     * @param desc1 Array del descriptor actual
     * @param desc2 Array del descriptor almacenado
     * @return
     */
    public static float kNN(float[] desc1, float[] desc2)
    {
        float d = 0;
        int i;
        float a;
        for (i = 0; i < desc1.length; ++i )
        {
            a = desc1[ i ] - desc2[ i ];
            d += a * a;
        }

        return ( float )Math.sqrt( d );
    }
     //--------------------------------------------------
    public int Probabilities(Model[] modelos)
    {
        int numG = modelos.length;
        int[] obs = new int[HGR_gui.GetObservations().size()];
        float[] Probabilities = new float[numG];
        int maxP =0;
        float maxPr=0;

        for(int p = 0; p<obs.length; p++)
        {
            obs[p] = HGR_gui.GetObservations().get(p);
        }

        for(int p = 0; p<numG; p++)
        {
            Probabilities[p] = hmm.Hmm.CalcularProbabilidad(modelos[p], obs);
        }
        
        
        for(int p = 0; p<numG; p++)
        {
            if(Probabilities[p]>maxPr)
            {
                maxPr=Probabilities[p];
                maxP = p;
            }
        }

        System.out.println("PROBABILIDAD_______________________:::::::  "+ maxPr );
        return maxP;
    }
}
//--------------------------------------------------
//FIN
//--------------------------------------------------